<?php
namespace WDB\Utils;

use     WDB,
        ArrayAccess;

class Arrays
{
    /**
     * Converts multi-dimensional array to one-dimensional.
     *
     * @param array
     * @return array
     */
    public static function linearize($arr)
    {
        $result = array();
        foreach ($arr as $elm)
        {
            if (is_array($elm))
            {
                $result = array_merge($result, self::linearize($elm));
            }
            else
            {
                $result[] = $elm;
            }
        }
        return $result;
    }

    /**
     * Returns last element of an array (or false on empty array). Similar to PHP end() function,
     * which raises "Indirect modification of overloaded property" notice for retrieving
     * element of an overloaded property of array type.
     *
     * @param array $arr
     * @return mixed
     */
    public static function last(array $arr)
    {
        if (count($arr) === 0) return FALSE;
        $keys = array_keys($arr);
        return $arr[$keys[count($keys)-1]];
    }

    /**
     * Returns first element of an array (or false on empty array). Similar to PHP reset(),
     * then current() function, which raises "Indirect modification of overloaded property"
     * notice for retrieving element of an overloaded property of array type.
     *
     * @param array $arr
     * @return mixed
     */
    public static function first(array $arr)
    {
        if (count($arr) === 0) return FALSE;
        $keys = array_keys($arr);
        return $arr[$keys[0]];
    }

    /**
     * Returns true when $arr is an array or instance of class implementing \ArrayAccess
     *
     * @param mixed $arr
     * @return bool
     */
    public static function isArrayAccessible($arr)
    {
        return is_array($arr) || $arr instanceof ArrayAccess;
    }

    /**
     * Returns array value from a multidimensional array with keys in $key separated by dot.
     * i.e. $array, 'config.foo.bar' returns $array['config']['foo']['bar']
     *
     * @param array input array
     * @param string dot separated key
     * @return mixed array value in the position defined by key
     *
     * @throws WDB\Exception\BadArgument
     */
    public static function dotPath(array $array, $key)
    {
        $path = explode('.', $key);
        $x = $array;
        foreach ($path as $p)
        {
            if (!isset($x[$p])) throw new WDB\Exception\BadArgument("Key $key not found");
            $x = $x[$p];
        }
        return $x;
    }

    /**
     * Remove an element from an array.
     *
     * @param array source array
     * @param mixed value to remove
     * @param bool remove all occurences (otherwise only first occurence is removed)
     */
    public static function remove(array &$array, $value, $all = TRUE)
    {
        foreach ($array as $key=>$val)
        {
            if ($val === $value)
            {
                unset($array[$key]);
                if (!$all) return;
            }
        }
    }

    /**
     * merges arrays with maintaining keys
     *
     * @param array $array1,...
     * @return array
     */
    public static function assocMerge()
    {
        $result = array();
        foreach (func_get_args() as $arr)
        {
            if ($arr)
            {
                foreach ($arr as $key=>$val)
                {
                    $result[$key] = $val;
                }
            }
        }
        return $result;
    }

    /**
     * Returns elements from first array that are not present in any other array.
     * In comparison with php's array_diff, this method compares values with typed comparison,
     * not casting them to strings, so it is better useable for objects.
     *
     * @param array $array1
     * @param array $array2,...
     * @return array
     */
    public static function diff(array $array1, array $array2)
    {
        $arrays = func_get_args();
        array_shift($arrays);
        foreach ($array1 as $key=>$val)
        {
            foreach ($arrays as $arrayn)
            {
                if (in_array($val, $arrayn, TRUE))
                {
                    unset($array1[$key]);
                    break;
                }
            }
        }
        return $array1;
    }
}
